package com.example.tcp.server.jt808;

import com.example.tcp.server.DecoderFactory;
import com.example.tcp.server.Frame;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@ConditionalOnProperty(prefix = "decoders.jt808", name = {"enable"}, havingValue = "true")
public class Jt808Decoder implements DecoderFactory.Decoder {

    private static final AttributeKey<Short> ATTR_PARSED_INFO = AttributeKey.valueOf("ATTR_PARSED_INFO");

    @Override
    public String id() {
        return "jt808";
    }

    @Override
    public boolean accept(ChannelHandlerContext ctx, ByteBuf in) {
        return in.readableBytes() >= 1 && in.getUnsignedByte(0) == 0x7e;
    }

    @Override
    public int length(ChannelHandlerContext ctx, ByteBuf buf) {
        short pos = 0;
        Channel ch = ctx.channel();
        if(ch.hasAttr(ATTR_PARSED_INFO)) {
            pos = ch.attr(ATTR_PARSED_INFO).get();
        }

        int flag = (pos & 0x8000) >> 15;
        int length = pos & 0x07ff;

        int total = buf.readableBytes();

        if(total < length) {
            return 0;
        }

        int size = 0;

        for(int i = length; i < total; ++i) {
            byte b = buf.getByte(i);
            if(b == 0x7e) {
                if(flag == 1) {
                    size = i + 1;
                    flag = length = 0;
                    break;
                } else {
                    flag = 1;
                }
            }

            length += 1;

            // 非法报文
            if(length >= 1024 * 5) {
                return -1;
            }
        }

        ch.attr(ATTR_PARSED_INFO).set((short)((flag << 15) | (length & 0x07ff)));

        return size;
    }

    @Override
    public Frame createFrame(ChannelHandlerContext ctx, ByteBuf buf) {

        ByteBuf translate = null;

        int size = buf.readableBytes();
        int total = buf.readableBytes();

        // 遍历报文，将0x7d 0x01 -> 0x7d, 0x7d 0x02 -> 0x7e
        for(int i = 0; i < size;) {
            byte b = buf.getByte(i);
            if(b == 0x7d && i + 1 < total - 1) { // total - 1 是因为原报文最后一个是0xaa
                byte next = buf.getByte(i+1);
                if(next == 0x01 || next == 0x02) {
                    if(translate == null) {
                        // total - 1是因为转换后的报文长度最大为原来的长度-1
                        translate = PooledByteBufAllocator.DEFAULT.buffer(total - 1);
                        translate.writeBytes(buf.slice(0, i));
                    }
                    if(next == 0x01) { //0x7d
                        translate.writeByte(0x7d);
                    } else { // 0x7e
                        translate.writeByte(0x7e);
                    }
                    i++;
                } else {
                    // 因为0x7d后面必然接0x01或0x02,所以这里一定不会进来,为了防止终端没有正确处理，这里加一个判断
                    if(translate != null) {
                        translate.writeByte(b);
                    }
                    log.error("device report frame, 0x55 next byte is not 0x01 or 0x02, frame: {}", ByteBufUtil.hexDump(buf));
                }
            } else {
                if(translate != null) {
                    translate.writeByte(b);
                }
            }
            i++;
        }

        if(translate != null) {
            buf.release();
        } else {
            translate = buf;
        }

        return new Jt808Frame(translate);
    }

}
